﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using GRTVPluginConsole.Listener.Processors;
using GRTVPluginConsole.Entity;

namespace GRTVPluginConsole.Listener
{
    class PluginListenerThread
    {
        // A delegate type for hooking up change notifications.
        public static event DriverRankReceived DriverRankEvent;
        public delegate void DriverRankReceived(object sender, DriverRank dirverRank);

        public static event NumberOfDriverReceived NumberOfDriverEvent;
        public delegate void NumberOfDriverReceived(object sender, int numberOfDrivers);
        
        public static event RankCompletedReceived RankCompletedEvent;
        public delegate void RankCompletedReceived(object sender, int session);

        public static event DriveEventReceived DriveEvent;
        public delegate void DriveEventReceived(object sender, Event driveEvent);

        public static event SessionEventReceived SessionEvent;
        public delegate void SessionEventReceived(object sender);

        public static event WeatherDataReceived WeatherDataEvent;
        public delegate void WeatherDataReceived(object sender, int step, string errorMessage);


        public static event WidgetStatusReceived WidgetStatusEvent;
        public delegate void WidgetStatusReceived(object sender, bool[] allStatus);

        public static event UserDataPathReceived UserDataPathEvent;
        public delegate void UserDataPathReceived(object sender, string userDataPath);

        static Thread myThread;
        public static Form1 callback;
        public static void Start()
        {
            // Déclaration du thread


            // Instanciation du thread, on spécifie dans le 
            // délégué ThreadStart le nom de la méthode qui
            // sera exécutée lorsque l'on appele la méthode
            // Start() de notre thread.
            myThread = new Thread(new ThreadStart(ThreadLoop));

            // Lancement du thread
            myThread.Start();
        }
        public static void Abort()
        {
            myThread.Abort();
        }
        private static void ThreadLoop()
        {
            int listeningPort = Program.configuration.serverPort;
            UdpClient udpServer = new UdpClient(listeningPort);
            // Tant que le thread n'est pas tué, on travaille
            while (Thread.CurrentThread.IsAlive)
            {
                IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
                byte[] data = udpServer.Receive(ref remoteEP); // listen on port 11000
                
                if (data.Length >= 0)
                {
                    Program.configuration.pluginAddress = remoteEP.Address;
                    // string code = Encoding.ASCII.GetString(data, 0, 6);
                    if (data[0] == 1)
                    { 
                        // user data path information
                        PluginUserDataPathProcessor processor1 = new PluginUserDataPathProcessor(remoteEP, data, UserDataPathEvent);
                        //                        new Thread(new ThreadStart(processor.ThreadLoop)).Start();
                        ThreadPool.QueueUserWorkItem(processor1.ThreadLoop);

                        if (data.Length == 1 || Program.configuration.cbTrackLocationProviderIndex != 2)
                        {
//                                callback.newSession();
                            // callback.reAskWeatherServer();
                        }
                        else
                        {

                            //PluginEnvironmentMessageProcessor processor = new PluginEnvironmentMessageProcessor(remoteEP, data, callback);
                            PluginEnvironmentMessageProcessor processor2 = new PluginEnvironmentMessageProcessor(remoteEP, data, WeatherDataEvent);
                            //                        new Thread(new ThreadStart(processor.ThreadLoop)).Start();
                            ThreadPool.QueueUserWorkItem(processor2.ThreadLoop);
                        }
                    }
                    else if (data[0] == 2)
                    {
                        // Events
                        if (data[1] == 1)
                        {
                            // Program.debug = code;
                            // ping identification
                            //PluginOvertakeProcessor processor = new PluginOvertakeProcessor(remoteEP, data, callback);
                            PluginOvertakeProcessor processor = new PluginOvertakeProcessor(remoteEP, data, DriveEvent);
                            //                        new Thread(new ThreadStart(processor.ThreadLoop)).Start();
                            ThreadPool.QueueUserWorkItem(processor.ThreadLoop);
                        }
                        else if (data[1] == 2)
                        {
//                            PluginFinishStatusProcessor processor = new PluginFinishStatusProcessor(remoteEP, data, callback);
                            PluginFinishStatusProcessor processor = new PluginFinishStatusProcessor(remoteEP, data, DriveEvent);
                            ThreadPool.QueueUserWorkItem(processor.ThreadLoop);
                        }
                        else if (data[1] == 3)
                        {
//                            PluginOffTrackProcessor processor = new PluginOffTrackProcessor(remoteEP, data, callback);
                            PluginOffTrackProcessor processor = new PluginOffTrackProcessor(remoteEP, data, DriveEvent);
                            ThreadPool.QueueUserWorkItem(processor.ThreadLoop);
                        }
                        else if (data[1] == 4)
                        {
                            //PluginSpinAroundProcessor processor = new PluginSpinAroundProcessor(remoteEP, data, callback);
                            PluginSpinAroundProcessor processor = new PluginSpinAroundProcessor(remoteEP, data, DriveEvent);
                            ThreadPool.QueueUserWorkItem(processor.ThreadLoop);
                        }
                    }
                    else if (data[0] == 3)
                    {
                        // Driver ranking
                       // PluginDriverRankingProcessor processor = new PluginDriverRankingProcessor(remoteEP, data, callback);
                        PluginDriverRankingProcessor processor = new PluginDriverRankingProcessor(remoteEP, data, DriverRankEvent);
                        ThreadPool.QueueUserWorkItem(processor.ThreadLoop);
                    }
                    else if (data[0] == 4)
                    {
                        // Number of drivers
                        //callback.SetNumberOfDrivers(BitConverter.ToInt32(data, 1));
                        if (NumberOfDriverEvent != null)
                        {
                            NumberOfDriverEvent(null, BitConverter.ToInt32(data, 1));
                            Program.configuration.trackName = getString(data, 5, 64);
                        }
                    }
                    else if (data[0] == 5)
                    {
                        // Number of drivers
                        //callback.SetNumberOfDrivers(BitConverter.ToInt32(data, 1));
                        if (RankCompletedEvent != null)
                        {
                            RankCompletedEvent(null, BitConverter.ToInt32(data, 1));
                        }
                    }
                    else if (data[0] == 6)
                    {
                        // All widget status
                        PluginAllWidgetStatusProcessor processor = new PluginAllWidgetStatusProcessor(remoteEP, data, WidgetStatusEvent);
                        ThreadPool.QueueUserWorkItem(processor.ThreadLoop);
                    }
                    else if (data[0] == 7)
                    {
                        if (SessionEvent != null)
                        {
                            SessionEvent(null);
                        }
                    }
                    else if (data[0] == 255)
                    {
                        // Response to the rF2TVi query
                        WeatherServerDataProcessor processor = new WeatherServerDataProcessor(remoteEP, data, callback);
                        ThreadPool.QueueUserWorkItem(processor.ThreadLoop);
                    }
                }
            }
        }

        private static string getString(byte[] data, int offset, int length)
        {
            for (int i = 0; i < length; i++)
            {
                if (data[offset + i] == 0)
                {
                    return Encoding.ASCII.GetString(data, offset, i);
                }
            }
            return Encoding.ASCII.GetString(data, offset, length);
        }
    
    }
}
